import time
import zmq
from zmq.eventloop.ioloop import IOLoop, PeriodicCallback
from zmq.eventloop.zmqstream import ZMQStream


# 服务器状态枚举
# 主机，等待同伴连接
STATE_PRIMARY = 1
# 备机，等待同伴连接
STATE_BACKUP = 2
# 激活态，处理应用程序请求
STATE_ACTIVE = 3
# 被动态，不接收请求
STATE_PASSIVE = 4

# 对话节点事件
# 主机
PEER_PRIMARY = 1
# 备机
PEER_BACKUP = 2
# 激活态
PEER_ACTIVE = 3
# 被动态
PEER_PASSIVE = 4
# 客户端请求
CLINENT_REQUEST = 5

# 发送状态信息的间隔时间
# 如果对方在两次心跳过后都没有应答，则视为断开
HEARTBEAT = 1000


class FSMError(Exception):
    pass


class BinaryStar(object):
    def __init__(self, primary, local, remote):
        self.ctx = zmq.Context()
        self.loop = IOLoop.instance()
        self.state = STATE_PRIMARY if primary else STATE_BACKUP

        self.event = None
        self.peer_expiry = 0

        self.voter_callback = None
        self.master_callback = None
        self.slave_callback = None

        self.voter_socket = None

        self.statepub = self.ctx.socket(zmq.PUB)
        self.statepub.bind(local)

        self.statesub = self.ctx.socket(zmq.SUB)
        self.statesub.setsockopt_string(zmq.SUBSCRIBE, '')
        self.statesub.connect(remote)

        self.statesub = ZMQStream(self.statesub)

        self.heartbeat = PeriodicCallback(self.send_state, HEARTBEAT)
        self.statesub.on_recv(self.recv_state)

    def start(self):
        self.update_peer_expiry()
        self.heartbeat.start()
        return self.loop.start()

    def send_state(self):
        self.statepub.send_string("%s" % self.state)

    def recv_state(self, msg):
        state = msg[0]
        if state:
            self.event = int(state)
            self.update_peer_expiry()
        self.execute_fsm()

    def update_peer_expiry(self):
        self.peer_expiry = time.time() + 2e-3 * HEARTBEAT

    def execute_fsm(self):
        accept = True
        if self.state == STATE_PRIMARY:
            if self.event == PEER_BACKUP:
                print("I: 已连接至备机（slave），可以作为master运行")
                self.state = STATE_ACTIVE
                if self.master_callback:
                    self.loop.add_callback(self.master_callback)
            elif self.event == PEER_ACTIVE:
                print("I: 已连接至备机（master），可以作为slave运行")
                self.state = STATE_PASSIVE
                if self.slave_callback:
                    self.loop.add_callback(self.slave_callback)
            elif self.event == CLINENT_REQUEST:
                if time.time() >= self.peer_expiry:
                    print("I: 作为master运行，收到客户端请求")
                    self.state = STATE_ACTIVE
                    if self.master_callback:
                        self.loop.add_callback(self.master_callback)
                else:
                    # 暂时不响应客户端-我们不知道备份当前是否由于成功的故障转移而处于活动状态
                    accept = False
        elif self.state == STATE_BACKUP:
            if self.event == PEER_ACTIVE:
                print("I: 已连接至主机（master），可以作为slave运行")
                self.state = STATE_PASSIVE
                if self.slave_callback:
                    self.loop.add_callback(self.slave_callback)
            elif self.event == CLINENT_REQUEST:
                accept = False
        elif self.state == STATE_ACTIVE:
            if self.event == PEER_ACTIVE:
                print("E: 严重错误：双master。正在退出")
                raise FSMError("双master")
        elif self.state == STATE_PASSIVE:
            if self.event == PEER_PRIMARY:
                print("I: 主机（slave）正在重启，可作为master运行")
                self.state = STATE_ACTIVE
            elif self.event == PEER_BACKUP:
                print("I: 备机（slave）正在重启，可作为master运行")
                self.state = STATE_ACTIVE
            elif self.event == PEER_PASSIVE:
                print("E: 严重错误：双slave。正在退出")
                raise FSMError("双slave")
            elif self.event == CLINENT_REQUEST:
                assert self.peer_expiry > 0
                if time.time() >= self.peer_expiry:
                    print("I: 同伴已死，转为激活态")
                    self.state = STATE_ACTIVE
                else:
                    accept = False
            if self.state == STATE_ACTIVE and self.master_callback:
                self.loop.add_callback(self.master_callback)
        return accept

    def register_voter(self, endpoint, type, handler):
        assert self.voter_callback is None

        socket = self.ctx.socket(type)
        socket.bind(endpoint)
        self.voter_socket = socket
        self.voter_callback = handler

        stream = ZMQStream(socket)
        stream.on_recv(self.voter_ready)

    def voter_ready(self, msg):
        self.event = CLINENT_REQUEST
        if self.execute_fsm():
            print("CLIENT REQUEST")
            self.voter_callback(self.voter_socket, msg)
        else:
            pass
